package controllers

import dao._
import implicits.Implicits._
import play.api.mvc._

import java.io.File
import java.nio.file.{Files, Paths}
import javax.inject.Inject
import scala.collection.JavaConverters._
import scala.concurrent.{Await, Future}
import scala.concurrent.duration.Duration
import implicits.Implicits._
import models.Tables._
import utils.Utils

import scala.concurrent.ExecutionContext.Implicits.global
import upickle.default._

import java.nio.charset.Charset

/** Created by yz on 2017/6/1.
  */
class DataInsertController @Inject() (cc: ControllerComponents)(
    implicit val browseGeneDao: BrowseGeneDao,
    implicit val geneExpDao: GeneExpDao,
    implicit val boxplotDataDao: BoxplotDataDao,
    implicit val mTissueDataDao: MTissueDataDao,
    implicit val hTissueDataDao: HTissueDataDao,
    implicit val genesDao: GenesDao,
    implicit val cellLineExprDao: CellLineExprDao,
    implicit val survExprDao: SurvExprDao,
    implicit val iccSurvExprDao: IccSurvExprDao,
    implicit val browseCircDao: BrowseCircDao,
    implicit val hcircCpmDao: HcircCpmDao,
    implicit val markerDao: MarkerDao,
    implicit val abundanceScDao: AbundanceScDao
) extends AbstractController(cc) {

  val parentDir = new File("E:\\gep_liver_database\\test")

  def insertBrowseGene = Action.async {
    val startTime = System.currentTimeMillis()
    val file = new File(parentDir, "Browse_gene_R3.txt")
    val lines = file.txtLines.map(_.trimQuote)
    val maps = lines.lineSeqMap
    val rows = maps.map { map =>
      BrowseGeneRow(
        geneSymbol = map("Gene Symbol"),
        species = map("Species"),
        geneType = map("Gene Type"),
        geneId = map("Gene ID"),
        related = map("Related circRNA"),
        fetalMedian = map("Fetal Median").toDouble,
        fetalFreq = map("Fetal Freq").toDouble,
        normalMedian = map("Normal Median").toDouble,
        normalFreq = map("Normal Freq").toDouble,
        fattyLiverMedian = map("Fatty Liver Median").toDoubleOption.getOrElse(Double.NaN),
        fattyIverFreq = map("Fatty Liver Freq").toDoubleOption.getOrElse(Double.NaN),
        nafldMedian = map("NAFLD Median").toDoubleOption.getOrElse(Double.NaN),
        nafldFreq = map("NAFLD Freq").toDoubleOption.getOrElse(Double.NaN),
        aldMedian = map("ALD Median").toDoubleOption.getOrElse(Double.NaN),
        aldFreq = map("ALD Freq").toDoubleOption.getOrElse(Double.NaN),
        vhMedian = map("Viral Hepatitis Median").toDoubleOption.getOrElse(Double.NaN),
        vhFreq = map("Viral Hepatitis Freq").toDoubleOption.getOrElse(Double.NaN),
        fibrosisMedian = map("Fibrosis Median").toDoubleOption.getOrElse(Double.NaN),
        fibrosisFreq = map("Fibrosis Freq").toDoubleOption.getOrElse(Double.NaN),
        cirrhosisMedian = map("Cirrhosis Median").toDoubleOption.getOrElse(Double.NaN),
        cirrhosisFreq = map("Cirrhosis Freq").toDoubleOption.getOrElse(Double.NaN),
        dnMedian = map("Dysplastic Nodule Median").toDoubleOption.getOrElse(Double.NaN),
        dnFreq = map("Dysplastic Nodule Freq").toDoubleOption.getOrElse(Double.NaN),
        hccMedian = map("HCC Median").toDoubleOption.getOrElse(Double.NaN),
        hccFreq = map("HCC Freq").toDoubleOption.getOrElse(Double.NaN),
        fibrolamellarMedian = map("Fibrolamellar Median").toDoubleOption.getOrElse(Double.NaN),
        fibrolamellarFreq = map("Fibrolamellar Freq").toDoubleOption.getOrElse(Double.NaN),
        iccMedian = map("ICC Median").toDoubleOption.getOrElse(Double.NaN),
        iccFreq = map("ICC Freq").toDoubleOption.getOrElse(Double.NaN),
        mixedMedian = map("Mixed HCC-ICC Median").toDoubleOption.getOrElse(Double.NaN),
        mixedFreq = map("Mixed HCC-ICC Freq").toDoubleOption.getOrElse(Double.NaN),
        hbMedian = map("HB Median").toDoubleOption.getOrElse(Double.NaN),
        hbFreq = map("HB Freq").toDoubleOption.getOrElse(Double.NaN),
        id = 0
      )
    }
    browseGeneDao.deleteAll().flatMap(_ => browseGeneDao.insertAll(rows)).map { _ =>
      Ok("success!" + "\t" + rows.size + "\t" + getTime(startTime))
    }
  }

  def insertBrowseCirc = Action.async {
    val startTime = System.currentTimeMillis()
    val file = new File(parentDir, "Browse_circ.txt")
    val lines = file.txtLines.map(_.trimQuote)
    val maps = lines.lineSeqMap
    val rows = maps.map { map =>
      val position = map("Position")
      val chr = position.split(":")(0)
      val start = position.split(":")(1).split("-")(0)
      val end = position.split(":")(1).split("-")(1)
      BrowseCircRow(
        circId = map("circID"),
        circBase = map("circBase ID"),
        species = map("Species"),
        position = position,
        strand = map("Strand"),
        geneSymbol = map("Gene Symbol"),
        geneType = map("Gene Type"),
        allMean = map("All Mean").toDouble,
        allFreq = map("All Freq").toDouble,
        fetalMean = map("Fetal Mean").toDoubleOption.getOrElse(Double.NaN),
        fetalFreq = map("Fetal Freq").toDoubleOption.getOrElse(Double.NaN),
        normalMean = map("Normal Mean").toDouble,
        normalFreq = map("Normal Freq").toDouble,
        nafldMean = map("NAFLD Mean").toDoubleOption.getOrElse(Double.NaN),
        nafldFreq = map("NAFLD Freq").toDoubleOption.getOrElse(Double.NaN),
        aldMean = map("ALD Mean").toDoubleOption.getOrElse(Double.NaN),
        aldFreq = map("ALD Freq").toDoubleOption.getOrElse(Double.NaN),
        vhMean = map("Viral Hepatitis Mean").toDoubleOption.getOrElse(Double.NaN),
        vhFreq = map("Viral Hepatitis Freq").toDoubleOption.getOrElse(Double.NaN),
        fibrosisMean = map("Fibrosis Mean").toDoubleOption.getOrElse(Double.NaN),
        fibrosisFreq = map("Fibrosis Freq").toDoubleOption.getOrElse(Double.NaN),
        cirrhosisMean = map("Cirrhosis Mean").toDoubleOption.getOrElse(Double.NaN),
        cirrhosisFreq = map("Cirrhosis Freq").toDoubleOption.getOrElse(Double.NaN),
        dnMean = map("Dysplastic Nodule Mean").toDoubleOption.getOrElse(Double.NaN),
        dnFreq = map("Dysplastic Nodule Freq").toDoubleOption.getOrElse(Double.NaN),
        hccMean = map("HCC Mean").toDoubleOption.getOrElse(Double.NaN),
        hccFreq = map("HCC Freq").toDoubleOption.getOrElse(Double.NaN),
        mixedMean = map("Mixed HCC-ICC Mean").toDoubleOption.getOrElse(Double.NaN),
        mixedFreq = map("Mixed HCC-ICC Freq").toDoubleOption.getOrElse(Double.NaN),
        iccMean = map("ICC Mean").toDoubleOption.getOrElse(Double.NaN),
        iccFreq = map("ICC Freq").toDoubleOption.getOrElse(Double.NaN),
        hbMean = map("HB Mean").toDoubleOption.getOrElse(Double.NaN),
        hbFreq = map("HB Freq").toDoubleOption.getOrElse(Double.NaN),
        chr = chr,
        start = start.toInt,
        end = end.toInt
      )
    }
    browseCircDao.deleteAll().flatMap(_ => browseCircDao.insertAll(rows)).map { _ =>
      Ok("success!" + "\t" + rows.size + "\t" + getTime(startTime))
    }
  }

  def insertGeneExp = Action {
    val startTime = System.currentTimeMillis()
    val dir = new File(parentDir, "exp")
    Utils.execFuture(geneExpDao.deleteAll())
    val file = new File(dir, "Exp_stat_all.txt")
    val lines = file.lines
    val kinds = List(
      "All",
      "Fetal",
      "Normal",
      "NAFLD",
      "Viral Hepatitis",
      "Cirrhosis",
      "ADJ_HCC",
      "HCC",
      "ADJ_ICC",
      "ICC",
      "Mixed",
      "ADJ_HB",
      "HB",
      "PBMC"
    )
    val headers = lines.head.mySplit("\t")
    val groupNum = 5000
    lines.drop(1).grouped(groupNum).zipWithIndex.foreach { case (eachLines, i) =>
      val maps = eachLines.map { line =>
        val columns = line.mySplit("\t")
        headers.zip(columns).toSeqMap

      }
      val rows = maps.flatMap { map =>
        kinds.map { kind =>
          val columnSuffix = kind match {
            case "Viral Hepatitis" => "Viral_Hepatitis"
            case x => x
          }
          GeneExpRow(
            gene = map("Gene"),
            lineage = map("Lineage"),
            cellType = map("Cell_Type"),
            avgExp = map(s"AvgExpr_${columnSuffix}"),
            freq = map(s"Freq_${columnSuffix}"),
            id = 0,
            phenotype = kind,
            suggestedLabels = map("Suggested_Labels"),
            clusterResolution = map("Cluster_Resolution")
          )
        }
      }
      val f = geneExpDao.insertAll(rows)
      Utils.execFuture(f)
      println((i + 1) * groupNum * 14, getTime(startTime))
    }

    println(s"insert ${file.getName} complete!", getTime(startTime))
    Ok("success!" + "\t" + getTime(startTime))
  }

  def insertBoxplotData = Action {
    val startTime = System.currentTimeMillis()
    val dir = new File(parentDir, "boxplot")
    Utils.execFuture(boxplotDataDao.deleteAll())
    dir.myListFiles.foreach { file =>
      val lines = file.txtLines.map(_.trimQuote)
      val maps = lines.lineSeqMap
      val rows = maps.map { map =>
        val phenotype = if (map("phenotype") == "Landscape") "All" else map("phenotype")
        BoxplotDataRow(
          cellType = map("Celltype"),
          med = map("Med"),
          mean = map("Mean"),
          avgExpr = map("AvgExpr"),
          q25 = map("Q25"),
          q75 = map("Q75"),
          min = map("Min"),
          max = map("Max"),
          freq = map("Freq"),
          gene = map("gene"),
          id = 0,
          cell = map("Exp.cells").toInt,
          phenotype = phenotype
        )
      }
      Utils.execFuture(boxplotDataDao.insertAll(rows))
      println(s"insert ${file.getName} complete!", getTime(startTime))
    }
    Ok("success!" + "\t" + getTime(startTime))
  }

  def insertGenes = Action.async {
    val startTime = System.currentTimeMillis()
    val file = new File(parentDir, "homologous_anno_R1.txt")
    val lines = file.txtLines.map(_.trimQuote)
    val maps = lines.lineSeqMap
    val rows = maps.map { map =>
      GenesRow(
        id = 0,
        mGene = map("mgene"),
        hGene = map("hgene")
      )
    }
    genesDao.deleteAll().flatMap(_ => genesDao.insertAll(rows)).map { _ =>
      Ok("success!" + "\t" + rows.size + "\t" + getTime(startTime))
    }
  }

  def insertMTissueData = Action.async {
    val startTime = System.currentTimeMillis()
    val file = new File(parentDir, "mbulk_expr.txt")
    val lines = file.txtLines.map(_.trimQuote)
    println(lines.map(_.size).distinct)
    val maps = lines.lineSeqMap
    val rows = maps.map { map =>
      val vMap = map.removed("gene")
      MTissueDataRow(
        gene = map("gene"),
        value = writeJs(vMap)
      )
    }
    mTissueDataDao.deleteAll().flatMap(_ => mTissueDataDao.insertAll(rows)).map { _ =>
      Ok("success!" + "\t" + rows.size + "\t" + getTime(startTime))
    }
  }

  def insertHTissueData = Action.async {
    val startTime = System.currentTimeMillis()
    val file = new File(parentDir, "hbulk_expr.txt")
    println("=start insert=")
    hTissueDataDao.deleteAll().map { x =>
      val lines = file.lines
      println(lines.size)
      val headers = lines.head.mySplit("\t").map(_.trimQuote)
      val groupNum = 1000
      lines.grouped(groupNum).toList.zipWithIndex.map { case (eachLines, i) =>
        val rows = eachLines.map { line =>
          val columns = line.mySplit("\t")
          val vMap = headers.drop(1).zip(columns.drop(1)).toSeqMap
          HTissueDataRow(
            gene = columns.head.trimQuote,
            value = writeJs(vMap)
          )
        }
        val f = hTissueDataDao.insertAll(rows)
        Utils.execFuture(f)
        println((i + 1) * groupNum, getTime(startTime))
      }
      Ok("success!" + "\t" + lines.size + "\t" + getTime(startTime))
    }
  }

  def insertCellLineExpr = Action.async {
    val startTime = System.currentTimeMillis()
    val file = new File(parentDir, "Cellline_expr.txt")
    println("=start insert=")
    cellLineExprDao.deleteAll().map { x =>
      val lines = file.lines
      println(lines.size)
      val headers = lines.head.mySplit("\t").map(_.trimQuote)
      val groupNum = 1000
      lines.grouped(groupNum).toList.zipWithIndex.map { case (eachLines, i) =>
        val rows = eachLines.map { line =>
          val columns = line.mySplit("\t")
          val vMap = headers.drop(1).zip(columns.drop(1)).toSeqMap
          CellLineExprRow(
            gene = columns.head.trimQuote,
            value = writeJs(vMap)
          )
        }
        val f = cellLineExprDao.insertAll(rows)
        Utils.execFuture(f)
        println((i + 1) * groupNum, getTime(startTime))
      }
      Ok("success!" + "\t" + lines.size + "\t" + getTime(startTime))
    }
  }

  def insertSurvExpr = Action.async {
    val startTime = System.currentTimeMillis()
    val file = new File(parentDir, "Gepliver_surv_expr.txt")
    println("=start insert=")
    survExprDao.deleteAll().map { x =>
      val lines = file.lines
      println(lines.size)
      val headers = lines.head.mySplit("\t").map(_.trimQuote)
      val groupNum = 1000
      lines.grouped(groupNum).toList.zipWithIndex.map { case (eachLines, i) =>
        val rows = eachLines.map { line =>
          val columns = line.mySplit("\t")
          val vMap = headers.drop(1).zip(columns.drop(1)).toSeqMap
          SurvExprRow(
            gene = columns.head.trimQuote,
            value = writeJs(vMap)
          )
        }
        val f = survExprDao.insertAll(rows)
        Utils.execFuture(f)
        println((i + 1) * groupNum, getTime(startTime))
      }
      Ok("success!" + "\t" + lines.size + "\t" + getTime(startTime))
    }
  }

  def insertIccSurvExpr = Action.async {
    val startTime = System.currentTimeMillis()
    val file = new File(parentDir, "fudan_icc_surv_expr.txt")
    println("=start insert=")
    iccSurvExprDao.deleteAll().map { x =>
      val lines = file.lines
      println(lines.size)
      val headers = lines.head.mySplit("\t").map(_.trimQuote)
      val groupNum = 1000
      lines.grouped(groupNum).toList.zipWithIndex.map { case (eachLines, i) =>
        val rows = eachLines.map { line =>
          val columns = line.mySplit("\t")
          val vMap = headers.drop(1).zip(columns.drop(1)).toSeqMap
          IccSurvExprRow(
            gene = columns.head.trimQuote,
            value = writeJs(vMap)
          )
        }
        val f = iccSurvExprDao.insertAll(rows)
        Utils.execFuture(f)
        println((i + 1) * groupNum, getTime(startTime))
      }
      Ok("success!" + "\t" + lines.size + "\t" + getTime(startTime))
    }
  }

  def insertHcircCpm = Action.async {
    val startTime = System.currentTimeMillis()
    val file = new File(parentDir, "hcirc_cpm_848S_R1.txt")
    println("=start insert=")
    hcircCpmDao.deleteAll().map { x =>
      val lines = file.lines
      println(lines.size)
      val headers = lines.head.mySplit("\t").map(_.trimQuote)
      val groupNum = 1000
      lines.grouped(groupNum).toList.zipWithIndex.map { case (eachLines, i) =>
        val rows = eachLines.map { line =>
          val columns = line.mySplit("\t")
          val vMap = headers.drop(1).zip(columns.drop(1)).toSeqMap
          HcircCpmRow(
            circId = columns.head.trimQuote,
            value = writeJs(vMap)
          )
        }
        val f = hcircCpmDao.insertAll(rows)
        Utils.execFuture(f)
        println((i + 1) * groupNum, getTime(startTime))
      }
      Ok("success!" + "\t" + lines.size + "\t" + getTime(startTime))
    }
  }

  def insertAbundanceSc = Action.async {
    val startTime = System.currentTimeMillis()
    val dir = new File(parentDir, "abundance")
    val file = new File(dir, "Abundance_SC.csv")
    abundanceScDao.deleteAll().map { x =>
      val maps = file.csvLines.lineSeqMap
      val rows = maps.map { map =>
        AbundanceScRow(
          id = 0,
          lineage = map("Lineage"),
          clusterResolution = map("Cluster_Resolution"),
          cellType = map("Cell_Type"),
          suggestedLabels = map("Suggested_Labels"),
          phenotype = map("Phenotype"),
          absoFreq = map("Abso_Freq"),
          absoOr = map("Abso_OR"),
          absoPval = map("Abso_pval"),
          relaFreq = map("Rela_Freq"),
          relaOr = map("Rela_OR"),
          relaPval = map("Rela_pval")
        )
      }
      val f = abundanceScDao.insertAll(rows)
      Utils.execFuture(f)
      Ok("success!" + "\t" + maps.size + "\t" + getTime(startTime))
    }
  }

  def insertMarker = Action.async {
    val startTime = System.currentTimeMillis()
    val file = new File(parentDir, "Marker_list.txt")
    markerDao.deleteAll().map { x =>
      val maps = file.txtLines().lineSeqMap
      val rows = maps.map { map =>
        MarkerRow(
          id = 0,
          lineage = map("Lineage"),
          cellType = map("Cell_Type"),
          marker = map("Marker"),
          avgLog2fc = map("Avg_log2FC").toDouble,
          pct1 = map("Pct.1").toDouble,
          pct2 = map("Pct.2").toDouble,
          pval = map("Pval").toDouble,
          pvalAdj = map("Pval_adj").toDouble,
          clusterResolution = map("Cluster_Resolution"),
          suggestedLabels = map("Suggested_Labels")
        )
      }
      val f = markerDao.insertAll(rows)
      Utils.execFuture(f)
      Ok("success!" + "\t" + maps.size + "\t" + getTime(startTime))
    }
  }

  def getTime(startTime: Long) = {
    val endTime = System.currentTimeMillis()
    (endTime - startTime) / 1000.0
  }

}
